#ifndef SIMPLE_SUPPORT_ITERATOR_OUT_FLATTEN_RANGE_HPP
#define SIMPLE_SUPPORT_ITERATOR_OUT_FLATTEN_RANGE_HPP

#include <iterator> // std::iterator_traits std::output_iterator_tag
#include <type_traits> // std::enable_if_t

#include "../algorithm/traits.hpp" // is_range_v
#include "../algorithm/range_wrappers.hpp" // support::copy

namespace simple::support
{

	template <typename It>
	struct out_flatten_range
	{
		using value_type = typename std::iterator_traits<It>::value_type;
		using difference_type = typename std::iterator_traits<It>::difference_type;
		using pointer = typename std::iterator_traits<It>::pointer;
		using reference = typename std::iterator_traits<It>::reference;
		using iterator_category = std::output_iterator_tag;
		It out;

		out_flatten_range(It out)
			: out(out)
		{}

		out_flatten_range& operator++() { return *this; }
		out_flatten_range operator++(int) { out_flatten_range prev = *this; ++(*this); return prev; }

		class proxy
		{
			It& out;

			proxy(It& out) : out(out) {}
			friend struct out_flatten_range;
			public:
			proxy() = delete;
			proxy(const proxy&) = delete;
			proxy(const proxy&&) = delete;
			proxy& operator=(const proxy&) = delete;
			proxy& operator=(const proxy&&) = delete;

			template <typename T, std::enable_if_t<is_range_v<T>>* = nullptr>
			const proxy& operator=(T&& x) const
			{
				out = support::copy(x, out);
				return *this;
			}

		};

		const proxy operator*()
		{
			return {out};
		}
	};

	// TODO out_flatten that can accept either range or a tuple, need the other versions cause a thing can be both a range and a tuple like

} // namespace simple::support

#endif /* end of include guard */
